#!/usr/bin/python

################################################################################
# This CGI is used to simulate different types of components in an HTML page
# that take different lengths of time. Here are the CGI params and legal values:
#
#     type=[gif|js|css|html|swf|xhr|jsxhr|jsiframe|cssiframe]
#           default is "gif"
#     sleep=N
#           N is a number of seconds for the server to wait before returning
#           the response
#           default is 0
#     jsdelay=N
#           This option is only applicable if the type is js, jsxhr, or
#           jsiframe. The JavaScript
#           that is returned will contain a loop that executes for N seconds.
#     expires=[-1|0|1]
#          -1   return an Expires header in the past
#           0   do not return an Expires header
#           1   return an Expires header in the future (default)
#     last=[-1|0]
#          -1   return a Last-Mo  dified header in the past
#           0   do not return a Last-Modified header
#     redir=1
#           1 means return a 302 redirect response that redirects right back
#           with the "redir=1" removed
#     cookie=N
#           The response sets a cookie that is N characters in length.
#     headers=1
#           The response is JavaScript with an object containing each HTTP
#           request header.
################################################################################

import logging
import os
import random
import re
import time
import urllib


STATUS_TEXT = {
  301: 'Moved Permanently',
  302: 'Moved Temporarily',
  303: 'See Other',
  304: 'Not Found',
  305: 'Use Proxy',
  306: '',
  307: 'Temporary Redirect',
}

DEFAULTS = {
  'type': 'gif',
  'content-type': 'image/gif',
  'sleep': 0,
  'jsdelay': 0,
  'expires': 1, # future Expires header
  'last': -1, # Last-Modified equal to a date in the past
  'status': 200, # Was 301, but that created some testing issues.
}


TYPE_HEADERS = {
  'css': 'text/css',
  'js': 'application/javascript',
  'html': 'text/html',
  'xhr': 'text/html',
  'xhrhtml': 'text/html',
  'cssiframe': 'text/html',
  'jsiframe': 'text/html',
  'jsxhr': 'text/html',
  'swf': 'application/x-shockwave-flash',
  'gif': 'image/gif'
}

WEEKDAYS = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat')

MONTHS = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct',
          'Nov', 'Dec')


PATH_TO_IMAGES = '%s/' % os.path.dirname(__file__)
IMAGES = ['starfish1.gif', 'barracuda.gif', 'turtle2.gif', 'crab3.gif']


def ParseParams(request):
  params = DEFAULTS.copy()
  params['querystring'] = request.query_string
  for key, val in request.params.items():
    params[key] = val

  # For redirects, we want to force a 300ish status if one is not sent.
  if params.has_key('redir'):
    if not params.has_key('status') or params['status'] == 200:
      params['status'] = 301

  logging.info('ParseParams: %s ' % params)
  return params


def GetCookieText(length):
  return 'A=%s; path=/' % ('a' * int(length))


def GetHostAndRequestPath(request):
  host = request.environ['HTTP_HOST']
  port = request.environ['SERVER_PORT']
  protocol = 'http://'
  if port == '443':
    protocol = 'https://'
  return '%s%s%s' % (protocol, host, request.path)


def GenHeaders(request, params):
  headers = {}
  logging.info('GenHeaders %s' % params)
  querystring = params['querystring']

  if params.has_key('redir'):
    if params.has_key('redirurl'):
      # RISKY - If a URL is provided, redirect to that URL.
      location = urllib.unquote(params['redirurl'])
    else:
      # Redir back to sleep.cgi without the redir param in the querystring.
      querystring = re.sub(r'[&]*redir=[^&]*', '', querystring)
      querystring = re.sub(r'^&', '', querystring)

      request_and_path = GetHostAndRequestPath(request)
      location = '%s?%s' % (request_and_path, querystring)

    # Use the more aggressive 301 response to try and promote caching
    # (since most browsers don't even cache 301s).
    if 301 <= params['status'] <= 307:
      headers['Content-Type'] = 'text/html'
      headers['Location'] = location

  elif params['type'] == 'font':
    c_type = 'font/ttf'
    if request.environ['HTTP_USER_AGENT'].search('MSIE'):
      c_type = 'application/octet-stream'
    headers['Content-Type'] = c_type

  else:
    headers['Content-Type'] = TYPE_HEADERS[params['type']]

  # Override to js if headers is set as a param.
  if params.has_key('headers'):
    headers['Content-Type'] = TYPE_HEADERS['js']

  if params.has_key('accessoff'):
    headers['Access-Control-Allow-Origin'] = '*'

  if params.has_key('cookie'):
    headers['Set-Cookie'] = GetCookieText(params['cookie'])

  # If include an Expires header (and Cache-Control) in the past or future
  if params.has_key('expires'):
    # aka 30 days
    expiration_seconds = 30 * 24 * 60 * 60
    if int(params['expires']) == -1:
      epoch = int(time.time()) - expiration_seconds
      headers['Expires'] = GetEpochTs(epoch)
    elif int(params['expires']) == 1:
      epoch = int(time.time()) + expiration_seconds
      headers['Expires'] = GetEpochTs(epoch)
    # expires == 0 means no Expires header

    # Cache-control
    if params.has_key('expires') and params['expires'] == -1:
      max_age = 0
    else:
      max_age = expiration_seconds
    headers['Cache-Control'] = 'public, max-age=%s' % max_age

  # If requested, include a Last-Modified header in the past.
  if int(params['last']) == -1:
    # We don't really have a timestamp for a file on disk
    # (since we generate the content on-the-fly),
		# so let's just pick a fixed time in the past since most items would
		# have a timestamp in the past.
		# noon 1/15/2006 GMT = 1137326400
		epoch = 1137326400
		headers['Last-Modified'] = GetEpochTs(epoch)

  #logging.info('HEADERS %s' % headers)
  return headers


def GetEpochTs(epoch, mod_year=True):
  #logging.info('GetEpochTs %s' % time.gmtime(epoch))
  (year, month, day, hour, minutes, sec, wday, jday, dst) = time.gmtime(epoch)
  ts = ("%s, %.2d %s %d %.2d:%.2d:%.2d GMT" %
        (WEEKDAYS[wday], day, MONTHS[month - 1], year, hour, minutes, sec))
  return ts


def GetJsDelayTypeContent(jsdelay, http_host, path):
  jsdelay_txt = ("var sleep_now = Number(new Date());\n\nfunction "
      "externalFunction1(i) { return i + 1; }\n\nwhile(sleep_now+%s000>"
      "Number(new Date())) { var tmp = sleep_now; }\nif ( 'function' == "
      "typeof(scriptSleepOnload) ) scriptSleepOnload('http://%s%s');\n" %
      (jsdelay, http_host, path))
  return jsdelay_txt


def GetJsXhrTypeContent(jsdelay, http_host, path):
  jsxhr = ("var sleep_now = Number(new Date()); while(sleep_now+%s000>Number("
      "new Date())) { var tmp = sleep_now; } if ( 'function' == "
      "typeof(scriptSleepOnload) ) scriptSleepOnload('http://%s%s');" %
      (jsdelay, http_host, path))
  return jsxhr


def GenCssTypeContent(size):
  return ('.sleepcgi { background: #EEE; color: #606; font-weight: bold; '
          'padding: 10px; };\n')


def GetXhrTypeContent():
  return 'XHR response from resource.cgi, epoch time = %s\n' % time.time()


def GetHtmlTypeContent(sleep):
  return ('<html><head><title>resource.cgi test page</title>'
      '</head><body bgcolor=#F0F0F0>'
      'This HTML document took %s seconds to return.'
      '</body></html>' % sleep)


def GetJsIframeTypeContent(jsdelay):
  return ('<html><head><title>resource.cgi test page</title>'
      '<script>var sleep_now = Number(new Date());'
      'while(sleep_now+%s000>Number(new Date())) { var tmp = sleep_now; }'
      '</script></head><body></body></html>' % jsdelay)


def GetCssIframeTypeContent(request):
  return ('<html><head><title>resource.cgi test page</title>'
      '<style>.sleepcgi { background: #EEE; color: #606; font-weight: bold;'
      'padding: 10px; }</style></head><body></body></html>')


def GetHeadersContent(request):
  content = ''
  keys = request.environ.keys()
  keys.sort()
  for key in keys:
    if content != '':
      content += ','

    content += "\n    '%s': '%s'" % (key,
                                     str(request.environ[key]).replace("'",'"'))

  content = "var aHeaders = {%s\n};\n" % content
  return content


def GenContent(request, params):

  if params.has_key('sleep'):
    time.sleep(float(params['sleep']))

  content = ''
  if params.has_key('headers'):
    content = GetHeadersContent(request)

  elif params['type'] == 'css':
    size = None
    if params.has_key('size'):
      size = params['size']
    content = GenCssTypeContent(size)

  elif params['type'] == 'js':
    jsdelay = params['jsdelay']
    content = GetJsDelayTypeContent(params['jsdelay'],
                                    request.environ['HTTP_HOST'],
                                    request.path_qs)

  elif params['type'] == 'xhr':
    content = GetXhrTypeContent()

  elif params['type'] == 'xhrhtml':
    content = GetXhrHtmlTypeContent(request)

  elif params['type'] == 'jsxhr':
    content = GetJsXhrTypeContent(params['jsdelay'],
                                 request.environ['HTTP_HOST'],
                                 request.path_qs)

  elif params['type'] == 'html':
    content = GetHtmlTypeContent(params['sleep'])

  elif params['type'] == 'jsiframe':
    content = GetJsIframeTypeContent(params['jsdelay'])

  elif params['type'] == 'cssiframe':
    content = GetCssIframeTypeContent()

  elif params['type'] == 'swf':
    f = open('%s%s' % (PATH_TO_IMAGES, 'sleep.swf'))
    content = f.read()
    f.close()

  else:
    if params.has_key('giffile'):
      gif_file = params['giffile']
    else:
      gif_file = IMAGES[random.randint(0, 3)]
    f = open('%s%s' % (PATH_TO_IMAGES, gif_file))
    content = f.read()
    f.close()

  return content


